package org.apache.james.mime4j.decoder;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

final class QuotedPrintableEncoder
{
  private static final byte CR = 13;
  private static final byte EQUALS = 61;
  private static final byte[] HEX_DIGITS;
  private static final byte LF = 10;
  private static final byte QUOTED_PRINTABLE_LAST_PLAIN = 126;
  private static final int QUOTED_PRINTABLE_MAX_LINE_LENGTH = 76;
  private static final int QUOTED_PRINTABLE_OCTETS_PER_ESCAPE = 3;
  private static final byte SPACE = 32;
  private static final byte TAB = 9;
  private final boolean binary;
  private final byte[] inBuffer;
  private int nextSoftBreak;
  private OutputStream out;
  private final byte[] outBuffer;
  private int outputIndex;
  private boolean pendingCR;
  private boolean pendingSpace;
  private boolean pendingTab;

  static
  {
    byte[] arrayOfByte = new byte[16];
    arrayOfByte[0] = 48;
    arrayOfByte[1] = 49;
    arrayOfByte[2] = 50;
    arrayOfByte[3] = 51;
    arrayOfByte[4] = 52;
    arrayOfByte[5] = 53;
    arrayOfByte[6] = 54;
    arrayOfByte[7] = 55;
    arrayOfByte[8] = 56;
    arrayOfByte[9] = 57;
    arrayOfByte[10] = 65;
    arrayOfByte[11] = 66;
    arrayOfByte[12] = 67;
    arrayOfByte[13] = 68;
    arrayOfByte[14] = 69;
    arrayOfByte[15] = 70;
    HEX_DIGITS = arrayOfByte;
  }

  public QuotedPrintableEncoder(int paramInt, boolean paramBoolean)
  {
    this.inBuffer = new byte[paramInt];
    this.outBuffer = new byte[paramInt * 3];
    this.outputIndex = 0;
    this.nextSoftBreak = 77;
    this.out = null;
    this.binary = paramBoolean;
    this.pendingSpace = false;
    this.pendingTab = false;
    this.pendingCR = false;
  }

  private void clearPending()
    throws IOException
  {
    this.pendingSpace = false;
    this.pendingTab = false;
    this.pendingCR = false;
  }

  private void encode(byte paramByte)
    throws IOException
  {
    if (paramByte == 10)
      if (this.binary)
      {
        writePending();
        escape(paramByte);
      }
    while (true)
    {
      while (true)
      {
        while (true)
        {
          while (true)
          {
            while (true)
            {
              while (true)
              {
                while (true)
                {
                  while (true)
                  {
                    while (true)
                    {
                      while (true)
                      {
                        while (true)
                        {
                          return;
                          if (this.pendingCR)
                          {
                            if (this.pendingSpace)
                              escape(32);
                            while (true)
                            {
                              do
                                while (true)
                                {
                                  lineBreak();
                                  clearPending();
                                }
                              while (!(this.pendingTab));
                              escape(9);
                            }
                          }
                          writePending();
                          plain(paramByte);
                        }
                        if (paramByte != 13)
                          break label111;
                        if (!(this.binary))
                          break;
                        escape(paramByte);
                      }
                      this.pendingCR = true;
                    }
                    label111: writePending();
                    if (paramByte != 32)
                      break label144;
                    if (!(this.binary))
                      break;
                    escape(paramByte);
                  }
                  this.pendingSpace = true;
                }
                label144: if (paramByte != 9)
                  break label173;
                if (!(this.binary))
                  break;
                escape(paramByte);
              }
              this.pendingTab = true;
            }
            label173: if (paramByte >= 32)
              break;
            escape(paramByte);
          }
          if (paramByte <= 126)
            break;
          escape(paramByte);
        }
        if (paramByte != 61)
          break;
        escape(paramByte);
      }
      plain(paramByte);
    }
  }

  private void escape(byte paramByte)
    throws IOException
  {
    int i = this.nextSoftBreak - 1;
    this.nextSoftBreak = i;
    if (i <= 3)
      softBreak();
    int j = paramByte & 0xFF;
    write(61);
    this.nextSoftBreak -= 1;
    write(HEX_DIGITS[(j >> 4)]);
    this.nextSoftBreak -= 1;
    write(HEX_DIGITS[(j % 16)]);
  }

  private void lineBreak()
    throws IOException
  {
    write(13);
    write(10);
    this.nextSoftBreak = 76;
  }

  private void plain(byte paramByte)
    throws IOException
  {
    int i = this.nextSoftBreak - 1;
    this.nextSoftBreak = i;
    if (i <= 1)
      softBreak();
    write(paramByte);
  }

  private void softBreak()
    throws IOException
  {
    write(61);
    lineBreak();
  }

  private void write(byte paramByte)
    throws IOException
  {
    byte[] arrayOfByte = this.outBuffer;
    int i = this.outputIndex;
    this.outputIndex = (i + 1);
    arrayOfByte[i] = paramByte;
    if (this.outputIndex >= this.outBuffer.length)
      flushOutput();
  }

  private void writePending()
    throws IOException
  {
    if (this.pendingSpace)
      plain(32);
    while (true)
    {
      do
        while (true)
        {
          clearPending();
          return;
          if (!(this.pendingTab))
            break;
          plain(9);
        }
      while (!(this.pendingCR));
      plain(13);
    }
  }

  void completeEncoding()
    throws IOException
  {
    writePending();
    flushOutput();
  }

  public void encode(InputStream paramInputStream, OutputStream paramOutputStream)
    throws IOException
  {
    initEncoding(paramOutputStream);
    while (true)
    {
      int i = paramInputStream.read(this.inBuffer);
      if (i <= -1)
        break;
      encodeChunk(this.inBuffer, 0, i);
    }
    completeEncoding();
  }

  void encodeChunk(byte[] paramArrayOfByte, int paramInt1, int paramInt2)
    throws IOException
  {
    for (int i = paramInt1; i < paramInt2 + paramInt1; ++i)
      encode(paramArrayOfByte[i]);
  }

  void flushOutput()
    throws IOException
  {
    if (this.outputIndex < this.outBuffer.length)
      this.out.write(this.outBuffer, 0, this.outputIndex);
    while (true)
    {
      this.outputIndex = 0;
      return;
      this.out.write(this.outBuffer);
    }
  }

  void initEncoding(OutputStream paramOutputStream)
  {
    this.out = paramOutputStream;
    this.pendingSpace = false;
    this.pendingTab = false;
    this.pendingCR = false;
    this.nextSoftBreak = 77;
  }
}